'''Define various convenience attributes, such as ``Node.dem`` for accessing DEM data of a node (equivalent to ``node->getData<DemData>()`` in c++).'''

from woo import core
from woo.core import Master
import sys, warnings
core.Field.nod=core.Field.nodes

## proxy for attribute-like access to Scene.labels
## http://stackoverflow.com/questions/16061041/proxy-class-for-accessing-other-class-items-as-attributes-getitem-infinite
class LabelMapperProxy(object):
    'Proxy for attribute-like access to :obj:`woo.core.LabelMapper`.'
    def __init__(self,mapper,prefix=''): self.__dict__['_mapper'],self.__dict__['_prefix']=mapper,prefix
    def __getattr__(self,key):
        # some mapper method was requested
        if key=='__dir__': return lambda: self._mapper.__dir__(self._prefix)
        if key.startswith('_'):
            if self._prefix: raise AttributeError('Attributes/methods starting with _ must be obtained from root LabelMapper or proxy (this instance has prefix "'+self._prefix+'")')
            else: return getattr(self._mapper,key)
        # submodule requested, return proxy with new prefix
        if self._mapper._whereIs(self._prefix+key)==core.LabelMapper.inMod:
            return LabelMapperProxy(self._mapper,prefix=self._prefix+key+'.')
        # return object
        return self._mapper[self._prefix+key]
    def __setattr__(self,key,val): self._mapper[self._prefix+key]=val
    def __delattr__(self,key): del self._mapper[self._prefix+key]
    # this is necessary for py3k
    def __dir__(self): return self._mapper.__dir__(prefix=self._prefix)
    # def __len__(self): return self._mapper.__len__(prefix=self._prefix)
    # def _newModule(self,mod): return self._mapper._newModule(self._prefix+mod)
def Scene_lab(scene):
    return LabelMapperProxy(scene.labels,prefix='')
core.Scene.lab=property(Scene_lab)





## deprecated classes
# if old class name is used, the new object is constructed and a warning is issued about old name being used
# keep chronologically ordered, oldest first; script/rename-class.py appends at the end
_deprecated={
    ('woo.dem','FlexFacet'):('woo.fem','Membrane'),
    ('woo.gl','Gl1_FlexFacet'):('woo.gl','Gl1_Membrane'),
    ('woo.dem','In2_FlexFacet_ElastMat'):('woo.fem','In2_Membrane_ElastMat'),
    #
    ('woo.dem','ParticleFactory'):('woo.dem','ParticleInlet'),
    ('woo.dem','RandomFactory'):('woo.dem','RandomInlet'),
    ('woo.dem','BoxFactory'):('woo.dem','BoxInlet'),
    ('woo.dem','CylinderFactory'):('woo.dem','CylinderInlet'),
    ('woo.dem','BoxFactory2d'):('woo.dem','BoxInlet2d'),
    ('woo.dem','ConveyorFactory'):('woo.dem','ConveyorInlet'),
    ('woo.dem','BoxDeleter'):('woo.dem','BoxOutlet'),
    #
    ('woo.gl','TraceGlRep'):('woo.dem','TraceGlRep'),
    ('woo.gl','ScalarRange'):('woo.core','ScalarRange'),
    ('woo.gl','NodeGlRep'):('woo.core','NodeVisRep'),
    #
    ### END_RENAMED_CLASSES_LIST ### (do not delete this line; scripts/rename-class.py uses it
}

def injectDeprecated():
    ''' Inject decprecated classes into woo modules as needed.
    '''
    proxyNamespace={}
    class warnWrap:
        def __init__(self,_old,_new):
            self.old,self.new=_old,_new
        def __call__(self,*args,**kw):
            warnings.warn("%s.%s was renamed to %s.%s; update your code!"%(self.old[0],self.old[1],self.new.__module__,self.new.__name__),DeprecationWarning,stacklevel=2);
            return self.new(*args,**kw)
    # deprecated names
    for deprec,curr in _deprecated.items():
        # try to import both modules
        try: mDep,mCurr=sys.modules[deprec[0]],sys.modules[curr[0]]
        except KeyError: continue
        # new name not found?!
        try: setattr(mDep,deprec[1],warnWrap(deprec,getattr(mCurr,curr[1])))
        except AttributeError: pass

injectDeprecated()

try:
    from woo import dem
    dem.DemField.par=dem.DemField.particles
    dem.DemField.con=dem.DemField.contacts
    dem.Particle.mat=dem.Particle.material
    core.Scene.dem=property(lambda s: dem.DemField.sceneGetField(s))
    core.Scene.hasDem=property(lambda s: dem.DemField.sceneHasField(s))
    # DemData defines those methods, which are used for transparent access to respective data field
    core.Node.dem=property(dem.DemData._getDataOnNode,dem.DemData._setDataOnNode)

    # those are deprecated
    def deprecWrapper(self,_oldName,_newName,_newFunc,*args,**kw):
        warnings.warn('The %s function is deprecated, use %s instead.'%(_oldName,_newName),DeprecationWarning,stacklevel=3)
        return _newFunc(self,*args,**kw)

    dem.ParticleContainer.append=lambda self, *args,**kw: deprecWrapper(self,'dem.ParticleContainer.append','dem.ParticleContainer.add',dem.ParticleContainer.add,*args,**kw)
    dem.ParticleContainer.appendClumped=lambda self, *args,**kw: deprecWrapper(self,'dem.ParticleContainer.appendClumped','dem.ParticleContainer.addClumped',dem.ParticleContainer.addClumped,*args,**kw)

    # nicer names
    import woo.utils
    dem.DemField.minimalEngines=staticmethod(woo.utils.defaultEngines)

    dem.Sphere.make=staticmethod(woo.utils.sphere)
    dem.Ellipsoid.make=staticmethod(woo.utils.ellipsoid)
    dem.Capsule.make=staticmethod(woo.utils.capsule)
    dem.Wall.make=staticmethod(woo.utils.wall)
    dem.Wall.makeBox=staticmethod(woo.utils.wallBox)
    dem.InfCylinder.make=staticmethod(woo.utils.infCylinder)
    dem.Cone.make=staticmethod(woo.utils.cone)
    dem.Facet.make=staticmethod(woo.utils.facet)
    dem.Rod.make=staticmethod(woo.utils.rod)
    dem.Truss.make=staticmethod(woo.utils.truss)
except ImportError:
    print('WARN: Woo could not import woo.dem...? (non-fatal but very suspicious)')
    core.Scene.hasDem=lambda o: False


try:
    from woo import mesh
    core.Node.mesh=property(mesh.MeshData._getDataOnNode,mesh.MeshData._setDataOnNode)
except ImportError: pass

try:
    from woo import fem
    fem.Membrane.make=staticmethod(woo.utils.membrane)
    fem.Tetra.make=staticmethod(woo.utils.tetra)
    fem.Tet4.make=staticmethod(woo.utils.tet4)
except ImportError:
    pass

try:
    from woo import sparc
    core.Scene.sparc=property(lambda s: sparc.SparcField.sceneGetField(s))
    core.Scene.hasSparc=property(lambda s: sparc.SparcField.sceneHasField(s))
    core.Node.sparc=property(sparc.SparcData._getDataOnNode,sparc.SparcData._setDataOnNode)
except ImportError:
    core.Scene.hasSparc=lambda o: False


try:
    import woo.cld
    core.Scene.clDem=property(lambda s: cld.CLDemField.sceneGetField(s))
    core.Scene.hasClDem=property(lambda s: woo.clDem.CLDemField.sceneHasField(s))
    core.Node.clDem=property(woo.cld.CLDemData._getDataOnNode,woo.cld.CLDemData._setDataOnNode)
except ImportError:
    core.Scene.hasClDem=lambda o: False


try:
    from woo import gl
    core.Node.gl=property(gl.GlData._getDataOnNode,gl.GlData._setDataOnNode)
except ImportError: pass

#try:
#    from woo import ancf
#    core.Node.ancf=property(ancf.AncfData._getDataOnNode,ancf.AncfData._setDataOnNode)
#except ImportError: pass
